import logging
import platform
import queue
import subprocess
import threading
import wave
import pyaudio
import json
from pydub import AudioSegment
import pygame
import sounddevice as sd
import numpy as np
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
import asyncio

logger = logging.getLogger(__name__)


class AbstractPlayer(object):
    """音频播放器抽象基类，定义了播放器的基本接口和功能"""
    
    def __init__(self, *args, **kwargs):
        """
        初始化抽象播放器
        
        Args:
            *args: 可变位置参数
            **kwargs: 可变关键字参数
        """
        super(AbstractPlayer, self).__init__()
        self.is_playing = False  # 当前是否正在播放
        self.play_queue = queue.Queue()  # 播放队列
        self._stop_event = threading.Event()  # 停止事件
        # 创建并启动消费者线程
        self.consumer_thread = threading.Thread(target=self._playing)
        self.consumer_thread.start()

    @staticmethod
    def to_wav(audio_file):
        """
        将音频文件转换为WAV格式
        
        Args:
            audio_file (str): 输入音频文件路径
            
        Returns:
            str: 转换后的WAV文件路径
        """
        tmp_file = audio_file + ".wav"
        wav_file = AudioSegment.from_file(audio_file)
        wav_file.export(tmp_file, format="wav")
        return tmp_file

    def _playing(self):
        """播放线程的主循环，从队列中获取音频数据并播放"""
        while not self._stop_event.is_set():
            data = self.play_queue.get()
            self.is_playing = True
            try:
                self.do_playing(data)
            except Exception as e:
                logger.error(f"播放音频失败: {e}")
            finally:
                self.play_queue.task_done()
                self.is_playing = False

    def play(self, data):
        """
        将音频文件添加到播放队列
        
        Args:
            data (str): 音频文件路径
        """
        logger.info(f"play file {data}")
        audio_file = self.to_wav(data)
        self.play_queue.put(audio_file)

    def stop(self):
        """停止当前播放并清空播放队列"""
        self._clear_queue()

    def shutdown(self):
        """关闭播放器，停止线程并清理资源"""
        self._clear_queue()
        self._stop_event.set()
        if self.consumer_thread.is_alive():
            self.consumer_thread.join()

    def get_playing_status(self):
        """
        获取播放状态
        
        Returns:
            bool: 如果正在播放或队列非空则返回True
        """
        return self.is_playing or (not self.play_queue.empty())

    def _clear_queue(self):
        """清空播放队列"""
        with self.play_queue.mutex:
            self.play_queue.queue.clear()

    def do_playing(self, audio_file):
        """
        播放音频的具体实现，由子类实现
        
        Args:
            audio_file (str): 要播放的音频文件路径
            
        Raises:
            NotImplementedError: 子类必须实现此方法
        """
        raise NotImplementedError("Subclasses must implement do_playing")

class PyaudioPlayer(AbstractPlayer):
    """使用PyAudio库播放音频的播放器"""
    
    def __init__(self, *args, **kwargs):
        """
        初始化PyAudio播放器
        
        Args:
            *args: 可变位置参数
            **kwargs: 可变关键字参数
        """
        super(PyaudioPlayer, self).__init__(*args, **kwargs)
        self.p = pyaudio.PyAudio()

    def do_playing(self, audio_file):
        """
        使用PyAudio播放WAV音频文件
        
        Args:
            audio_file (str): 要播放的WAV音频文件路径
        """
        chunk = 1024  # 每次读取的音频数据块大小
        try:
            with wave.open(audio_file, 'rb') as wf:
                # 打开音频流
                stream = self.p.open(format=self.p.get_format_from_width(wf.getsampwidth()),
                                     channels=wf.getnchannels(),
                                     rate=wf.getframerate(),
                                     output=True)
                data = wf.readframes(chunk)
                # 逐块播放音频
                while data:
                    stream.write(data)
                    data = wf.readframes(chunk)
                # 停止并关闭流
                stream.stop_stream()
                stream.close()
            logger.debug(f"播放完成：{audio_file}")
        except Exception as e:
            logger.error(f"播放音频失败: {e}")

    def stop(self):
        """停止播放并终止PyAudio"""
        super().stop()
        if self.p:
            self.p.terminate()


class WebSocketPlayer(AbstractPlayer):
    """通过WebSocket发送音频到前端的播放器"""
    
    def __init__(self, *args, **kwargs):
        """
        初始化WebSocket播放器
        
        Args:
            *args: 可变位置参数
            **kwargs: 可变关键字参数
        """
        super(WebSocketPlayer, self).__init__(*args, **kwargs)
        self.websocket = None  # WebSocket连接
        self.loop = None  # 事件循环
        self.playing_status = False  # 播放状态
        self.lock = threading.Lock()  # 线程锁

    def init(self, websocket: WebSocket, loop):
        """
        初始化WebSocket连接和事件循环
        
        Args:
            websocket (WebSocket): WebSocket连接对象
            loop: asyncio事件循环
        """
        self.websocket = websocket
        self.loop = loop

    def get_playing_status(self):
        """
        获取播放状态
        
        Returns:
            bool: 当前播放状态
        """
        return self.playing_status

    def set_playing_status(self, status):
        """
        设置播放状态
        
        Args:
            status (bool): 要设置的播放状态
        """
        self.playing_status = status

    def do_playing(self, audio_file):
        """
        通过WebSocket发送音频数据到前端
        
        Args:
            audio_file (str): 要发送的音频文件路径
        """
        try:
            with open(audio_file, "rb") as f:
                wav_data = f.read()
            logger.info(f"websocket 发送音频文件：{audio_file}")

            # 在事件循环中发送音频数据
            asyncio.run_coroutine_threadsafe(
                self.websocket.send_bytes(wav_data),
                self.loop
            )

        except Exception as e:
            logger.error(f"播放音频失败: {e}")

    def interrupt(self):
        """通过WebSocket发送中断命令到前端"""
        try:
            if self.websocket and self.websocket.client_state.value == 1:  # 1 = CONNECTED
                asyncio.run_coroutine_threadsafe(
                    self.websocket.send_text(json.dumps({"type": "interrupt"})),
                    self.loop
                )
            else:
                logger.warning("尝试中断时 WebSocket 未连接")
        except Exception as e:
            logger.error(f"发送中断命令失败: {e}")

    def send_messages(self, messages):
        """
        通过WebSocket发送消息到前端
        
        Args:
            messages (str or list): 要发送的消息或消息列表
        """
        logger.info(f"send_messages: {messages}")
        if not self.websocket or self.websocket.client_state.value != 1:  # 1 = CONNECTED
            logger.warning("发送消息时 WebSocket 未连接")
            return

        data = {
            "type": "update_dialogue",
            "data": messages if isinstance(messages, list) else [messages]
        }
        try:
            asyncio.run_coroutine_threadsafe(
                self.websocket.send_text(json.dumps(data)),
                self.loop
            )
        except Exception as e:
            logger.error(f"发送消息失败: {e}")

    def stop(self):
        """停止播放器并发送中断命令"""
        try:
            if self.websocket and self.websocket.client_state.value == 1:  # 1 = CONNECTED
                asyncio.run_coroutine_threadsafe(
                    self.websocket.send_text(json.dumps({"type": "interrupt"})),
                    self.loop
                )
        except Exception as e:
            logger.error(f"停止播放器失败: {e}")


def create_instance(class_name, *args, **kwargs):
    """
    根据类名创建播放器实例
    
    Args:
        class_name (str): 要创建的类名
        *args: 传递给构造函数的参数
        **kwargs: 传递给构造函数的关键字参数
        
    Returns:
        AbstractPlayer: 播放器实例
        
    Raises:
        ValueError: 当类名不存在时抛出
    """
    # 获取类对象
    cls = globals().get(class_name)
    if cls:
        # 创建并返回实例
        print(f"Creating instance of {class_name} with args: {args}, kwargs: {kwargs}")
        return cls(*args, **kwargs)
    else:
        raise ValueError(f"Class {class_name} not found")


def main():
    """主测试函数"""
    import os
    import tempfile
    import time
    
    # 设置日志级别
    logging.basicConfig(level=logging.INFO)
    
    # 创建一个测试用的WAV文件
    def create_test_wav(filename, duration=1, frequency=440, sample_rate=44100):
        """创建测试WAV文件"""
        # 生成正弦波
        t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
        audio_data = (np.sin(2 * np.pi * frequency * t) * 32767).astype(np.int16)
        
        # 写入WAV文件
        with wave.open(filename, 'w') as wf:
            wf.setnchannels(1)  # 单声道
            wf.setsampwidth(2)  # 16位
            wf.setframerate(sample_rate)
            wf.writeframes(audio_data.tobytes())
    
    # 测试不同的播放器
    def test_player(player_class, test_file):
        """测试单个播放器"""
        print(f"\n=== 测试 {player_class.__name__} ===")
        
        try:
            # 创建播放器实例
            player = player_class()
            
            # 测试播放状态
            print(f"初始播放状态: {player.get_playing_status()}")
            
            # 播放音频
            print(f"播放音频: {test_file}")
            player.play(test_file)
            
            # 等待一段时间检查状态
            time.sleep(0.5)
            print(f"播放中状态: {player.get_playing_status()}")
            
            # 等待播放完成
            max_wait = 5  # 最大等待时间（秒）
            start_time = time.time()
            while player.get_playing_status() and (time.time() - start_time) < max_wait:
                time.sleep(0.1)
            
            print(f"播放完成状态: {player.get_playing_status()}")
            
            # 测试连续播放
            print("测试连续播放...")
            for i in range(2):
                player.play(test_file)
                time.sleep(0.1)  # 短暂延迟
            
            # 等待第一个播放完成
            time.sleep(1)
            print(f"队列播放状态: {player.get_playing_status()}")
            
            # 测试停止功能
            print("测试停止功能...")
            player.stop()
            time.sleep(0.5)
            print(f"停止后状态: {player.get_playing_status()}")
            
            # 关闭播放器
            player.shutdown()
            print(f"{player_class.__name__} 测试完成")
            
        except Exception as e:
            print(f"{player_class.__name__} 测试失败: {e}")
    
    # 创建临时目录和测试文件
    with tempfile.TemporaryDirectory() as temp_dir:
        test_wav = os.path.join(temp_dir, "test.wav")
        create_test_wav(test_wav, duration=2)  # 创建2秒的测试音频
        
        print("创建测试音频文件:", test_wav)
        print("文件大小:", os.path.getsize(test_wav), "bytes")
        
        # 测试支持的播放器（排除需要特殊环境的）
        players_to_test = [PyaudioPlayer, WebSocketPlayer]
        
        for player_class in players_to_test:
            test_player(player_class, test_wav)
        
        # 测试工厂函数
        print("\n=== 测试工厂函数 ===")
        try:
            factory_player = create_instance("WebSocketPlayer")
            print("工厂函数创建成功:", type(factory_player).__name__)
            factory_player.shutdown()
        except Exception as e:
            print("工厂函数测试失败:", e)
        
        print("\n=== 所有测试完成 ===")


if __name__ == "__main__":
    main()